Skip to contentMethod: CollectionChangeDetector(ChangeDetector, ChangeManagerImpl, MetamodelProvider)
1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.sessions.change;
16:
17: import cz.cvut.kbss.jopa.sessions.MetamodelProvider;
18: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
19:
20: import java.util.*;
21:
22: /**
23: * Checks for changes in a collection. By changes it is understood:
24: * <pre>
25: * <ul>
26: * <li>Different number of elements,</li>
27: * <li>Different order of elements in a collection with deterministic ordering semantics (List, Queue,
28: * <b>NOT!</b> Set)</li>
29: * <li>Different elements in the collection</li>
30: * <li>Changed elements in the collection (their attributes)</li>
31: * </ul>
32: * </pre>
33: */
34: class CollectionChangeDetector implements ChangeDetector {
35:
36: private final ChangeDetector changeDetector;
37:
38: private final ChangeManagerImpl changeManager;
39: private final MetamodelProvider metamodelProvider;
40:
41: CollectionChangeDetector(ChangeDetector changeDetector, ChangeManagerImpl changeManager,
42: MetamodelProvider metamodelProvider) {
43: this.changeDetector = changeDetector;
44: this.changeManager = changeManager;
45: this.metamodelProvider = metamodelProvider;
46: }
47:
48: @Override
49: public Changed hasChanges(Object clone, Object original) {
50: assert clone != null;
51: assert original != null;
52:
53: Collection<?> origCol = (Collection<?>) original;
54: Collection<?> cloneCol = (Collection<?>) clone;
55: if (origCol.size() != cloneCol.size()) {
56: return Changed.TRUE;
57: }
58: if (origCol.isEmpty()) {
59: return Changed.FALSE;
60: }
61: if (origCol instanceof Set) {
62: return setChanged(cloneCol, origCol);
63: } else {
64: return orderedCollectionChanged(cloneCol, origCol);
65: }
66: }
67:
68: private Changed orderedCollectionChanged(Collection<?> clone, Collection<?> original) {
69: Iterator<?> itOrig = original.iterator();
70: Iterator<?> itClone = clone.iterator();
71: boolean changes = false;
72: while (itOrig.hasNext() && !changes) {
73: Object cl = itClone.next();
74: Object orig = itOrig.next();
75: final Changed ch = changeDetector.hasChanges(orig, cl);
76: switch (ch) {
77: case TRUE:
78: changes = true;
79: break;
80: case FALSE:
81: changes = false;
82: break;
83: case UNDETERMINED:
84: changes = changeManager.hasChangesInternal(orig, cl);
85: break;
86: }
87: }
88: return Changed.fromBoolean(changes);
89: }
90:
91: /**
92: * When checking for set changes, we have to make sure different element order does not mean a change.
93: * <p>
94: * Therefore, we first order the elements in a predictable way and then compare the elements.
95: */
96: private Changed setChanged(Collection<?> clone, Collection<?> original) {
97: assert !clone.isEmpty();
98: assert !original.isEmpty();
99:
100: final Class<?> elementType = clone.iterator().next().getClass();
101: final List<?> cloneList = new ArrayList<>(clone);
102: final Comparator<Object> comparator = getSetComparator(elementType);
103: cloneList.sort(comparator);
104: final List<?> originalList = new ArrayList<>(original);
105: originalList.sort(comparator);
106: return orderedCollectionChanged(cloneList, originalList);
107: }
108:
109: /**
110: * For managed types, the comparator uses identifier's hashCode, for all other types hashCode is used directly.
111: */
112: private Comparator<Object> getSetComparator(Class<?> elemType) {
113: if (metamodelProvider.isTypeManaged(elemType)) {
114: return (o1, o2) -> {
115: final Object keyOne = EntityPropertiesUtils.getPrimaryKey(o1, metamodelProvider.getMetamodel());
116: final Object keyTwo = EntityPropertiesUtils.getPrimaryKey(o2, metamodelProvider.getMetamodel());
117: if (keyOne == null || keyTwo == null) {
118: return 0;
119: }
120: return Integer.compare(keyOne.hashCode(), keyTwo.hashCode());
121: };
122: } else {
123: return (o1, o2) -> Integer.compare(o1.hashCode(), o2.hashCode());
124: }
125: }
126: }